POJ 3977 Subset(折半搜索)

题意:

$N\le 35个数的集合S,|A_i|\le 10^{15}$
$求1个非空子集S’,使得|\sum_{S’\in S}A_i|最小,同时使得元素个数最少$

分析:

$数据范围都告诉你是折半搜索辣$
$显然预处理2^{N/2}个非空子集的答案先存起来排序(特么窝忘记排序了调一年)$
$之后枚举后2^{N-N/2}个非空子集,假设当前子集是sum, cnt$
$那么显然应该去枚举-sum,但是由于可能不存在$
$所以应该lower_bound这个(-sum, -INF)一波$
$显然找到的就是大于等于的那个最小的,并且cnt也是最小的$
$之后这个lower_bound-1就是小于的那个$
$但是这个时候由于重复的,找到cnt是最大的,不是可选解$
$显然取出这个值sum’,再去lower_bound查找一下(sum’, -INF)$
$就是要求的最小的cnt的那个辣$
$时间复杂度O(n2^nlog2^n)$

代码:

//
//  Created by TaoSama on 2016-07-29
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n;
typedef long long LL;
LL a[40];

LL ABS(const LL& x) {
    if(x < 0) return -x;
    return x;
}

void checkAns(pair<LL, LL>& ans, pair<LL, LL> rhs, LL sum, LL cnt) {
    LL lftSum = rhs.first, lftCnt = rhs.second;
    ans = min(ans, make_pair(ABS(sum + lftSum), cnt + lftCnt));
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    while(scanf("%d", &n) == 1 && n) {
        for(int i = 0; i < n; ++i) scanf("%lld", a + i);

        pair<LL, LL> ans((LL)1e18, (LL)1e18);

        int half = n >> 1, lft = n - half;
        vector<pair<LL, LL> > v;
        for(int i = 1; i < 1 << half; ++i) {
            LL sum = 0, cnt = 0;
            for(int j = 0; j < half; ++j) {
                if(i >> j & 1) {
                    ++cnt;
                    sum += a[j];
                }
            }
            v.push_back(make_pair(sum, cnt));
            checkAns(ans, make_pair(0, 0), sum, cnt);
        }
        sort(v.begin(), v.end());

        for(int i = 1; i < 1 << lft; ++i) {
            LL sum = 0, cnt = 0;
            for(int j = 0; j < lft; ++j) {
                if(i >> j & 1) {
                    ++cnt;
                    sum += a[half + j];
                }
            }
            checkAns(ans, make_pair(0, 0), sum, cnt);

            int x = lower_bound(v.begin(), v.end(), make_pair(-sum,
                                (LL) - INF)) - v.begin();
            if(x != v.size()) checkAns(ans, v[x], sum, cnt);

            if(x) {
                --x;
                x = lower_bound(v.begin(), v.end(), make_pair(v[x].first,
                                (LL) - INF)) - v.begin();
                checkAns(ans, v[x], sum, cnt);
            }
        }
        printf("%lld %lld\n", ans.first, ans.second);
    }

#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}